#include "Client.h"

Client::Command* commands[] = {
	new Client::Command("quit", "Exit the program", "/q or /quit"),
	new Client::Command("help", "Displays this help file.", "/h or /help"),
	new Client::Command("connect", "Connects to a server.", "/c or /connect [ip or domain]"),
	new Client::Command("disconnect", "Disconnects from current server.", "/d or /disconnect"),
	new Client::Command("list", "Lists the users on server.", "/l or /list")
};

Client::Client(void)
{
	//	filePort = FILE_PORT;
	cursor = 0;
	scrollPos = 0;
	characters = 0;
	message[0] = '\0';
	host = NULL;
	isRunning = true;

	// Empty switcher set
	FD_ZERO(&switcher);
	// Watch STDIN for input.
	FD_SET(fileno(stdin), &switcher);

	max_fd = fileno(stdin);
}

Client::~Client( void )
{
	system("PAUSE");
	disconnect();
	// Teardown client
}

#ifdef __WIN32__
void Client::run(void)
{
	DWORD dwWait = 0;
	Dispatch *packet = NULL;
	unsigned int num_handles = 1;
	int handle = 0;

	HANDLE handles[] = {
		GetStdHandle(STD_INPUT_HANDLE),
		WSA_INVALID_EVENT
	};

	while (isRunning)
	{
		// Check to see if we have a host.
		// If we do, create an event, and
		// configure it with listening to
		// the socket for a 'ready to read'
		if (host != NULL)
		{
			num_handles = 2;
			handles[1] = WSACreateEvent();
			WSAEventSelect((SOCKET)textSock, handles[1], FD_READ);

		} else num_handles = 1;

		// Wait on the handles, (STDIN if no connection, STDIN and socket if there is)
		dwWait = WaitForMultipleObjects(num_handles,	//number of event objects
										handles,	// array of event objects
										FALSE,			// does not wait for all
										INFINITY);		// Waits forever

		// Which handle is ready?
		handle = dwWait - WAIT_OBJECT_0;

		if (handle < 0 || handle > (num_handles - 1)) //NUM_HANDLES = 2
		{
			addLine(0, "Index out of range.");
			printf("Index out of range.\n");

			// Event was the socket having something to read.
		} else if (handle == 1)
		{
			// New dispatch
			packet = new Dispatch();

			// Read from the socket
			if ( packet->receive(textSock) == -1 )
			{
				addLine(0, "Error recving from socket stream.");

			} else if( packet->getBytes() == 0 )
			{
				addLine(0, "Server disconnected");
				disconnect();

			} else processDispatch(packet);

			delete packet;

			// Otherwise, it was an input event.
		} else catchInput();

		updateWindow();
	}
}
#else
void Client::run(void)
{
	fd_set handles;
	Dispatch *packet = NULL;

	// Clear out entire set
	FD_ZERO(&handles);

	while (isRunning)
	{
		FD_ZERO(&switcher);
		FD_SET(fileno(stdin), &switcher);

		if(host != NULL)
		{
			FD_SET(textSock, &switcher);
			select(textSock+1, &switcher, NULL, NULL, 0);
		} else {
			select(fileno(stdin)+1, &switcher, NULL, NULL, 0);
		}

		if(FD_ISSET(fileno(stdin), &switcher))
		{
			catchInput();
		} else if(host != NULL)
		{
			if(packet->receive(textSock) == -1)
			{
				addLine(0, "Error recving from socket stream.");
			} else if(packet->getBytes() == 0)
			{
				addLine(0, "Server disconnected");
				disconnect();
			} else {
				processDispatch(packet);
			}
		}
		updateWindow();
	}

}
#endif

bool Client::connectTo( const std::string ip)
{
	// STEP: Make sure disconnected before connecting
	// STEP: Check ip by using gethostbyame to see if connting to a valid host
	// STEP: check to see if textSock is a valid socket

	addLine(0, "Opening a connection...");

    // STEP: Create connection to host via information
    // from gethostbyname() using sockaddr_in etc

		// Add socket to master set
		FD_SET(textSock, &switcher);

		// Set the maximum file descriptor
		max_fd = max_fd > textSock ? max_fd : textSock;

		// STEP: Notify server upon successful connection of client name.
		// HINT: Make request to server to use `username'

		addLine(0, "Client initialization successfull.");

	return true;
}

void Client::disconnect()
{
	// STEP: Make sure your connected
	// STEP: Tell the server you are disconnecting
	// STEP: Close the socket

	host = NULL;
	addLine(0, "Connection Closed.");

	// Remove socket from master set.
	FD_CLR(textSock, &switcher);
}

bool Client::validIP( const std::string ip)
{
	// STEP: Make sure that the IP address is in a valid format
}

void Client::processDispatch (Dispatch *dispatch)
{
	// STEP: Process each Flag of dispatch
	// HINT: Use a switch statement for each DispatchFlag

	// STEP: Keep track of corrupt or misunderstood packets
	// HINT: use CORRUPT and default: cases to handle that.

}

void Client::processCommands(char* message)
{
    // TODO: Create User commands for the client
}

void Client::requestUserList( void )
{
	// TODO: get a list of the users from the server
	// HINT: Use LIST_USERS as dispatch flag
}

void Client::info(const char *args)
{
	// TODO: Display to user information about connection and any other relevant information.
}

void Client::help(const char *args)
{
	int cmds = (sizeof(commands))/sizeof(Client::Command*);

	if (!strcmp(args, ""))
	{
		if (1 == 0)
			addLine(0, "No help file found, help has been disabled.");

		addLine(0, "\t=======================================");
		addLine(0, "\tHelp! Below are the available commands.");
		addLine(0, "\tType `/help [command]' for more information.");
		addLine(0, "\t=======================================");

		// For converting int to string.
		std::stringstream out;
		for (unsigned int i = 0; i < cmds; i++)
		{
			// Empty the converter
			out.str("");
			out << i+1;
			addLine(0, std::string("\t" + out.str() + ".\t`" + commands[i]->cmd) + "'\t\t" + commands[i]->dsc);
		}

		addLine(0, "\t=======================================");
		addLine(0, "\tEnd help. I hope you're no longer lost!");
		addLine(0, "\t=======================================");

	} else
	{
		for (int j = 0; j < cmds; j++)
		{
			if (std::string(args) == commands[j]->cmd)
			{
				addLine(0, std::string("Command: ") + commands[j]->cmd);
				addLine(0, commands[j]->dsc);
				addLine(0, std::string("Use: ") + commands[j]->use);
				return;
			}
		}
		addLine(0, std::string("No help avalible for ") + args);
	}
}

void Client::quit(const char* quitMessage)
{
	disconnect();
	isRunning = false;
}

void Client::nick(const char* nick)
{
	// If the client has a connection open, we'll
	// want to tell the server of the nick change
	// Then the server needs to send back a message
	// Saying: old_nick is now known as new_nick
	username = nick;

	if (host != NULL)
	{
		Dispatch packet = Dispatch(CLIENT_REQUEST_NAME, username);
		packet.release(textSock);
		addLine(0, std::string("Username changed to ") + username);
	} else {
		addLine(0, std::string("Username set to ") + username);
	}
}

void Client::readLine (char* line, FILE* file)
{
	char* temp = line;
	int val = -1;
	while (val != '\r' && val != '\n')
	{
		val = getc(file);
		if (val == EOF)
		{
			if (feof(file))	break;
			else			temp = NULL;
		}
		*temp++ = val;
	}
	*temp = '\0';
}
